package com.oig.sys.file.service.impl;

import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonConstants;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.ServiceException;
import com.oig.common.module.SysPageInfo;
import com.oig.common.module.SysRequest;
import com.oig.sys.file.dao.ITbFileInfoDao;
import com.oig.sys.file.dto.BusFileInfoDto;
import com.oig.sys.file.dto.FileInfoDto;
import com.oig.sys.file.entity.TbFileInfo;
import com.oig.sys.file.service.ITbBusFileConfigService;
import com.oig.sys.file.service.ITbFileInfoService;
import com.oig.sys.file.vo.BusFileConfigVo;
import com.oig.sys.file.vo.FileInfoVo;
import com.oig.sys.minio.core.MinIOTemplate;
import com.oig.sys.minio.util.ObjectNameUtil;
import io.minio.errors.ErrorResponseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @author wuxl
 */
@Slf4j
@Service
public class TbFileInfoServiceImpl implements ITbFileInfoService {

	private final ITbFileInfoDao tbFileInfoDao;
	private final ITbBusFileConfigService busFileConfigService;
	private final MinIOTemplate minIOTemplate;

	@Autowired
	public TbFileInfoServiceImpl(ITbFileInfoDao tbFileInfoDao,
								 ITbBusFileConfigService busFileConfigService,
								 MinIOTemplate minIOTemplate){
		 this.tbFileInfoDao = tbFileInfoDao;
		 this.busFileConfigService = busFileConfigService;
		 this.minIOTemplate = minIOTemplate;
	}



	@Transactional
	@Override
	public FileInfoVo addFileInfo(FileInfoDto fileInfoDto) {
		TbFileInfo fileInfo = new TbFileInfo();
		BeanUtils.copyProperties(fileInfoDto, fileInfo);
		fileInfo.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		BusFileConfigVo config = busFileConfigService.findConfig(fileInfo.getBusinessType(), fileInfo.getRelationType()) ;
		try {
			if (fileInfo.getFileId().startsWith(minIOTemplate.getTempBucket())){
				String oldObjectName = fileInfo.getFileId().substring(minIOTemplate.getTempBucket().length()+1);
				log.info("objName:{}--cfg:{}",oldObjectName, config);
				String newFileId;
				try {
					newFileId = this.minIOTemplate.copyAndDelTempObject(oldObjectName, config.getBucketName());
					log.info("newFileId：{}",newFileId);
				}catch (ErrorResponseException e){
					log.info("文件没有下载到!",e);
					//这里做一下补偿，因为看日志其实已经移桶成功，文件已经存到业务桶了，那么去业务桶去找，如果能找到业务可以执行成功，否则抛出异常
					if(this.minIOTemplate.objectNameExists(config.getBucketName(),oldObjectName)){
						newFileId=config.getBucketName()+ObjectNameUtil.FILE_SPLIT_PATH+oldObjectName;
						log.info("newFileId：{}",newFileId);
					}else {
						log.info("捕捉到ErrorResponseException异常",e);
						throw new ServiceException("文件下载失败！");
					}
				}
				fileInfo.setFileId(newFileId);
				fileInfo.setFileUrl(this.getFileUrl(config.getBucketName(),oldObjectName));
			} else if (!fileInfo.getFileId().startsWith(config.getBucketName())){
				String oldBucket = fileInfo.getFileId().substring(0,fileInfo.getFileId().indexOf(ObjectNameUtil.FILE_SPLIT_PATH)) ;
				String oldObjectName = fileInfo.getFileId().substring(fileInfo.getFileId().indexOf(ObjectNameUtil.FILE_SPLIT_PATH)+1);
                String newFileId;
				try {
				 newFileId = this.minIOTemplate.copyObject(oldBucket, oldObjectName, config.getBucketName(), oldObjectName);
				log.info("newFileId：{}",newFileId);
				}catch (ErrorResponseException e){
					log.info("文件没有下载到2!",e);
					//这里做一下补偿，因为看日志其实已经移桶成功，文件已经存到业务桶了，那么去业务桶去找，如果能找到业务可以执行成功，否则抛出异常
					if(this.minIOTemplate.objectNameExists(config.getBucketName(),oldObjectName)){
						newFileId=config.getBucketName()+ObjectNameUtil.FILE_SPLIT_PATH+oldObjectName;
						log.info("newFileId：{}",newFileId);
					}else {
						log.info("捕捉到ErrorResponseException异常2",e);
						throw new ServiceException("文件下载失败！");
					}
				}
                fileInfo.setFileId(newFileId);
				fileInfo.setFileUrl(this.getFileUrl(config.getBucketName(),oldObjectName));
			}
		} catch (Exception e){
			log.error("文件处理失败",e);
			throw new ServiceException("文件处理失败") ;
		}
		if (StringUtils.isBlank(fileInfo.getDownloadFlag())){
			fileInfo.setDownloadFlag(CommonEnum.FlagEnum.TRUE_FLAG.getCode());
		}
		fileInfo.setLastUpdateId(fileInfo.getCreateId());
		fileInfo.setLastUpdateName(fileInfo.getCreateName());
		fileInfo.setLastUpdateTime(fileInfo.getCreateTime());
		fileInfo.setFileUploadId(fileInfo.getCreateId());
		fileInfo.setFileUploadCode(fileInfo.getCreateCode());
		fileInfo.setFileUploadName(fileInfo.getCreateName());
		fileInfo.setFileUploadTime(fileInfo.getCreateTime());
		this.tbFileInfoDao.save(fileInfo) ;
		FileInfoVo fileInfoVo = new FileInfoVo();
		BeanUtils.copyProperties(fileInfo, fileInfoVo);
		return fileInfoVo;
	}

	private String getFileUrl(String newBucketName, String objectName){
		String expUrl = this.minIOTemplate.getObjectURL(newBucketName, objectName);
		return expUrl.substring(0,expUrl.lastIndexOf("?"));
	}

	@Transactional
	@Override
	public FileInfoVo updateFileInfo(FileInfoDto fileInfoDto) {
		TbFileInfo fileInfo = this.tbFileInfoDao.getByFileInfoId(fileInfoDto.getId());
		try {
			if (fileInfo.getFileId()!=null && !fileInfo.getFileId().equals(fileInfoDto.getFileId())){
				//如果文件重新上传了
				BusFileConfigVo config = busFileConfigService.findConfig(fileInfo.getBusinessType(), fileInfo.getRelationType()) ;
				String bucket = fileInfo.getFileId().substring(0,fileInfo.getFileId().indexOf(ObjectNameUtil.FILE_SPLIT_PATH)) ;
				String objectName = fileInfo.getFileId().substring(fileInfo.getFileId().indexOf(ObjectNameUtil.FILE_SPLIT_PATH)+1);
				this.minIOTemplate.removeObject(bucket,objectName);	//删除旧文件
				fileInfo.setFileId(fileInfoDto.getFileId());
				fileInfo.setFileUrl(fileInfoDto.getFileUrl());
				//新文件挪桶
				if (fileInfoDto.getFileId().startsWith(minIOTemplate.getTempBucket())){
					String oldObjectName = fileInfoDto.getFileId().substring(minIOTemplate.getTempBucket().length()+1);
					String newFileId = this.minIOTemplate.copyAndDelTempObject(oldObjectName, config.getBucketName());
					fileInfo.setFileId(newFileId);
					fileInfo.setFileUrl(this.getFileUrl(config.getBucketName(),oldObjectName));
				}
				fileInfo.setFileUploadId(fileInfoDto.getFileUploadId());
				fileInfo.setFileUploadCode(fileInfoDto.getFileUploadCode());
				fileInfo.setFileUploadName(fileInfoDto.getFileUploadName());
				fileInfo.setFileUploadTime(new Date());
			}
		} catch (Exception e){
			log.error("文件处理失败",e);
			throw new ServiceException("文件处理失败") ;
		}
		BeanUtils.copyProperties(fileInfoDto, fileInfo,"status","businessType","businessOrderId","relationType","relationId","fileId","fileUrl",
				"systemId", "systemName","createId","createName","createTime","fileUploadTime","fileUploadId","fileUploadCode","fileUploadName","tenantId", "tenantName");
		//fileInfo.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		if (StringUtils.isBlank(fileInfo.getDownloadFlag())){
			fileInfo.setDownloadFlag(CommonEnum.FlagEnum.TRUE_FLAG.getCode());
		}
		this.tbFileInfoDao.save(fileInfo) ;
		FileInfoVo fileInfoVo = new FileInfoVo();
		BeanUtils.copyProperties(fileInfo, fileInfoVo);
		return fileInfoVo;
	}

	@Transactional
	@Override
	public List<FileInfoVo> updateRelationAllFileInfo(BusFileInfoDto busFileInfoDto) {
		List<FileInfoVo> fileInfoVoList = new ArrayList<>();
		List<Integer> idList = new ArrayList<>();
		for (FileInfoDto fileInfoDto : busFileInfoDto.getList()){
			FileInfoVo fileInfoVo ;
			if (fileInfoDto.getId()==null){
				fileInfoVo =this.addFileInfo(fileInfoDto) ;
			} else {
				fileInfoVo =this.updateFileInfo(fileInfoDto);
			}
			idList.add(fileInfoVo.getId());
			fileInfoVoList.add(fileInfoVo);
		}
		List<TbFileInfo> delList ;
		if (idList.isEmpty()){
			delList = tbFileInfoDao.findByRelationId(busFileInfoDto.getBusinessType(),busFileInfoDto.getBusinessOrderId(),
                    busFileInfoDto.getRelationType(),busFileInfoDto.getRelationId());
		} else {
			delList = tbFileInfoDao.findByRelationIdExIds(busFileInfoDto.getBusinessType(),busFileInfoDto.getBusinessOrderId(),
                    busFileInfoDto.getRelationType(),busFileInfoDto.getRelationId(),idList);
		}
		if (delList!=null && !delList.isEmpty()){
			//删除
			for (TbFileInfo fileInfo : delList){
				this.delFile(fileInfo);
			}
		}
		return fileInfoVoList;
	}


	private void delFile(TbFileInfo fileInfo){
		try {
			if (fileInfo.getFileId()!=null){
				String objectName = fileInfo.getFileId().substring(fileInfo.getFileId().indexOf(ObjectNameUtil.FILE_SPLIT_PATH)+1);
				String bucket = fileInfo.getFileId().substring(0,fileInfo.getFileId().indexOf(ObjectNameUtil.FILE_SPLIT_PATH)) ;
				this.minIOTemplate.removeObject(bucket,objectName);	//删除旧文件
			}
			tbFileInfoDao.delete(fileInfo);
		} catch (Exception e){
			log.error("文件删除失败",e);
			throw new ServiceException("文件删除失败") ;
		}
	}



	@Transactional
	@Override
	public void delRelationAllFileInfo(BusFileInfoDto busFileInfoDto) {
		List<TbFileInfo> delFileList = tbFileInfoDao.findByRelationId(busFileInfoDto.getBusinessType(),busFileInfoDto.getBusinessOrderId(),
				busFileInfoDto.getRelationType(),busFileInfoDto.getRelationId());
		if (delFileList!=null && !delFileList.isEmpty()){
			//删除
			for (TbFileInfo fileInfo : delFileList){
				this.delFile(fileInfo);
			}
		}
	}



	@Transactional
	@Override
	public FileInfoVo cancelFileInfo(Integer id) {
		TbFileInfo fileInfo = this.tbFileInfoDao.getByFileInfoId(id);
		fileInfo.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
		this.tbFileInfoDao.save(fileInfo) ;
		FileInfoVo fileInfoVo = new FileInfoVo();
		BeanUtils.copyProperties(fileInfo, fileInfoVo);
		return fileInfoVo;
	}

	@Transactional
	@Override
	public void delFileInfo(Integer id) {
		TbFileInfo fileInfo = this.tbFileInfoDao.getByFileInfoId(id);
		this.delFile(fileInfo);
	}

	@Override
	public FileInfoVo getFileInfo(Integer id) {
		TbFileInfo fileInfo = this.tbFileInfoDao.getByFileInfoId(id);
		FileInfoVo fileInfoVo = new FileInfoVo();
		BeanUtils.copyProperties(fileInfo, fileInfoVo);
		return fileInfoVo;
	}

    @Override
	public SysPageInfo<FileInfoVo> queryList(SysRequest<FileInfoDto> fileInfoRequest) {
		Pageable pageable = fileInfoRequest.pageable();
		if (pageable.getSort().isUnsorted()){
			pageable.getSort().and(Sort.by(Sort.Direction.DESC, "lastUpdateTime"));
		}
		Specification<TbFileInfo> specification = new Specification<TbFileInfo>() {
			@Override
			@Nullable
			public Predicate toPredicate(@NonNull Root<TbFileInfo> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
				List<Predicate> predicateList = new ArrayList<>();
				if (StrUtil.isNotBlank(fileInfoRequest.getParamDto().getBusinessType())){
					Path<String> businessTypePath = root.get("businessType");
					predicateList.add(criteriaBuilder.equal(businessTypePath, fileInfoRequest.getParamDto().getBusinessType()));
				}
				String businessOrderId = fileInfoRequest.getParamDto().getBusinessOrderId();
				if (StrUtil.isNotBlank(businessOrderId)) {
					List<String> businessOrderIdList;
					if (businessOrderId.contains(CommonConstants.SEMICOLON_STR)) {
						businessOrderIdList = new ArrayList<>(Arrays.asList(businessOrderId.split(CommonConstants.SEMICOLON_STR)));
					} else {
						businessOrderIdList = Collections.singletonList(businessOrderId);
					}
					Path<String> businessOrderIdPath = root.get("businessOrderId");
					CriteriaBuilder.In<String> cbIn = criteriaBuilder.in(businessOrderIdPath);
					businessOrderIdList.forEach(cbIn::value);
					predicateList.add(cbIn);
				}
				if (StrUtil.isNotBlank(fileInfoRequest.getParamDto().getRelationType())) {
					Path<String> relationTypePath = root.get("relationType");
					predicateList.add(criteriaBuilder.equal(relationTypePath, fileInfoRequest.getParamDto().getRelationType()));
				}
				String relationId = fileInfoRequest.getParamDto().getRelationId();
				if (StrUtil.isNotBlank(relationId)) {
					String[] split = relationId.split(CommonConstants.SPLIT_STR);

					Path<String> relationIdPath = root.get("relationId");
					CriteriaBuilder.In<String> in = criteriaBuilder.in(relationIdPath);
					for (String reId : split) {
						in.value(reId);
					}
					predicateList.add(in);
				}
				if (StrUtil.isNotBlank(fileInfoRequest.getParamDto().getFileNumber())) {
					Path<String> fileNumberPath = root.get("fileNumber");
					predicateList.add(criteriaBuilder.equal(fileNumberPath, fileInfoRequest.getParamDto().getFileNumber()));
				}
				if (StrUtil.isNotBlank(fileInfoRequest.getParamDto().getDownloadFlag())) {
					Path<String> downloadFlagPath = root.get("downloadFlag");
					predicateList.add(criteriaBuilder.equal(downloadFlagPath, fileInfoRequest.getParamDto().getDownloadFlag()));
				}
/*				if (StrUtil.isNotBlank(fileInfoRequest.getParamDto().getFormalType())){
					Path<String> formalTypePath = root.get("formalType");
					predicateList.add(criteriaBuilder.equal(formalTypePath, fileInfoRequest.getParamDto().getFormalType()));
				}*/
				String formalType=fileInfoRequest.getParamDto().getFormalType();
				if (StrUtil.isNotBlank(formalType)) {
					List<String> formalTypeList;
					if (formalType.contains(CommonConstants.SEMICOLON_STR)) {
						formalTypeList = new ArrayList<>(Arrays.asList(formalType.split(CommonConstants.SEMICOLON_STR)));
					} else {
						formalTypeList = Collections.singletonList(formalType);
					}
					Path<String> formalTypePath = root.get("formalType");
					CriteriaBuilder.In<String> cbIn = criteriaBuilder.in(formalTypePath);
					formalTypeList.forEach(cbIn::value);
					predicateList.add(cbIn);
				}

				if (StrUtil.isNotBlank(fileInfoRequest.getParamDto().getStatus())){
					Path<String> statusPath = root.get("status");
					predicateList.add(criteriaBuilder.equal(statusPath, fileInfoRequest.getParamDto().getStatus()));
				}
				if (fileInfoRequest.getParamDto().getTenantId()!=null) {
					Path<Integer> tenantIdPath = root.get("tenantId");
					predicateList.add(criteriaBuilder.equal(tenantIdPath, fileInfoRequest.getParamDto().getTenantId()));
				}
				criteriaQuery.where(predicateList.toArray(new Predicate[0]));
				return criteriaQuery.getRestriction();
			}
		} ;
		Page<TbFileInfo> page = this.tbFileInfoDao.findAll(specification, pageable) ;
		List<FileInfoVo> voList = new ArrayList<>();
		for (TbFileInfo fileInfo : page.getContent()){
			FileInfoVo fileInfoVo = new FileInfoVo();
			BeanUtils.copyProperties(fileInfo,fileInfoVo);
			voList.add(fileInfoVo);
		}
		SysPageInfo<FileInfoVo> sysPageInfo = new SysPageInfo<>(fileInfoRequest.getPageNo(), fileInfoRequest.getPageSize(), page.getTotalElements());
		sysPageInfo.setList(voList);
		return sysPageInfo;
	}


}
